/*
 * Copyright 2002-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.fjsei.yewu.resolver.base;

import com.querydsl.core.types.Predicate;
import md.cm.base.Companies;
import md.cm.base.Company;
import md.cm.base.CompanyPi;
import md.cm.unit.QUnit;
import md.cm.unit.Unit;
import md.cm.unit.UnitPi;
import md.cm.unit.Units;
import md.system.User;
import org.fjsei.yewu.graphql.IdMapper;
import org.fjsei.yewu.jpa.QBeanMy;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.web.ProjectedPayload;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.BatchMapping;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.execution.BatchLoaderRegistry;
import org.springframework.stereotype.Controller;
import reactor.core.publisher.Mono;

import java.util.*;

/**默认Unit模型的属性； graphQL接口;
 * 底下批处理方案还不是一种的。 注释掉的也是1个方案。
 * 每一个graphQL的Object对象type都需单独声明一个XxxController implements IdMapper<type> {} 否则Id都无法转换成Relay要的GlobalID的。
 * */

@Controller
public class UserController implements IdMapper<User> {
	private final Units units;
	private final Companies companies;

//	public UnitController(Units units, Companies companies) {
//		this.units = units;
//		this.companies = companies;
//	}

	/** registry参数：IDEA报红却能运行成功，实际运行注入DefaultBatchLoaderRegistry;
     *  消除报红，测试平台Units没有报红 https://blog.csdn.net/zy103118/article/details/84523452
	 * 添加了 @ComponentScan({"md","org.fjsei.yewu"}) 以后: Units报红消除了。
	 * */
	public UserController(BatchLoaderRegistry registry, Units units, Companies companies) {
		this.units = units;
		this.companies = companies;
//		//启动时刻仅注册，内部函数真正运行是在底下CompletableFuture<Company> company(Unit unit, DataLoader<UUID, Company> loader)收集到所有ID以后的！
//		registry.forTypePair(UUID.class, Company.class).registerMappedBatchLoader((companyIds, env) -> {
//		//	return Mono.just(mapkv); //类型来源registerMappedBatchLoader(BiFunction<Set<K>, BatchLoaderEnvironment, Mono<Map<K, V>>> loader);
//			return Mono.defer(() ->{
//				List<Company>  companyList=companies.findAllById(companyIds);
//				Map<UUID, Company>  mapkv = companyList.stream().collect(Collectors.toMap(Company::getId, Function.identity(), (key1, key2) -> key2));
//				return Mono.just(mapkv);
//			} );
//		});
	}
	/**这个会在父辈graphql的接口执行中的内省阶段才会执行：收集批量执行的ID，然后才是运行上面registerMappedBatchLoader注册的内部函数。
	 * */
//	@SchemaMapping
//	public CompletableFuture<Company> company(Unit unit, DataLoader<UUID, Company> loader) {
//		if(null!=unit.getCompany())
//			return loader.load(unit.getCompany().getId());	   //一对多的关联 loadMany()? 需要从相反方向查找IDs：性能有问题啊？感觉不适合这种机制;
//		else
//			return null;
//	}


	/**实际是 Unit.company_67 的批量操作。
	 **/
    @BatchMapping
    public  Mono<Map<UnitPi, Company>>  company_67(List<Unit> units) {
		return Mono.defer(() ->{
			//一对一情况下可以的： #但是多对一：假设若多个unit 对应 同一个的company? 按以下处理做法的就行不通： companyId2Unit: Map<companyId=>List<Unit>>;
			List<UUID>  companyIds = new ArrayList<>();
			HashMap<UUID, UnitPi>  companyId2Unit= new HashMap<>();
			for(UnitPi unit: units){
				CompanyPi company=unit.getCompany();		//UnitPi是含有了getCompany()的最小化接口类
				if(null!=company) {
					companyIds.add(company.getId());
					companyId2Unit.put(company.getId(), unit);
				}
			}
			List<Company>  companyList= companies.findAllById(companyIds);	 //批量执行SQL;
			HashMap<UnitPi, Company>  mapkv= new HashMap<>();
			for(int i=0; i<companyList.size(); i++) {
				Company company=companyList.get(i);
				mapkv.put(companyId2Unit.get(company.getId()), company);
			}
			for(int i=0; i<companyIds.size(); i++) {		 //？ 需要对companyId上一步SQL查询: 没实际Company对象的设置吗 ?
				UnitPi unit=companyId2Unit.get(companyIds.get(i));
				if(!mapkv.containsKey(unit))	mapkv.put(unit, null);
			}
			return Mono.just(mapkv);
			} );
    }

/*	@QueryMapping
	public Project project2(@Argument String slug) {
		return client.fetchProject(slug);
	}*/

	//@SchemaMapping
	// Page<Book> books(@QuerydslPredicate(root = Book.class) Predicate predicate)
	//				Pageable pageable , @Argument Map<String, Object> parameters
	/* (@Arguments("bookInput")  BookInputProjection bookInput)

{
    books(name:"做法是",id:"fff") {
        id,name,url  snapshotsEnabled
    }
}
DataLoader<String, Book> loader;	 @ContextValue Predicate predicate,
	* */

	//@QuerydslPredicate只能用在MVC rest URL参数上面。报错Cannot invoke "Object.getClass()" because "source" is null
	/*
	Fluent Query API support for Querydsl支持： https://docs.spring.io/spring-data/commons/docs/current/reference/html/#core.extensions.querydsl
		@SuppressWarnings("unchecked")
		public Iterable<R> get(DataFetchingEnvironment env) {
			return this.executor.findBy(buildPredicate(env), query -> {
				FetchableFluentQuery<R> queryToUse = (FetchableFluentQuery<R>) query;

				if (this.sort.isSorted()){
					queryToUse = queryToUse.sortBy(this.sort);
				}

				if (requiresProjection(this.resultType)){
					queryToUse = queryToUse.as(this.resultType);
				}
				else {
					queryToUse = queryToUse.project(buildPropertyPaths(env.getSelectionSet(), this.resultType));
				}

				return queryToUse.all();
			});
		}
	* */

	/**会单独呈现在Collection<MappingInfo> findHandlerMethods()映射中。
	 * 不采用默认查询接口的； 需要自定义过滤参数规则，需要分页的：目前最佳方案。
	 * unitRepository目前最佳方案
	 * @QueryMapping 与 @BatchMapping 注解的若接口函数同名字报错！
	 * */
	@QueryMapping
	public <T> Slice<UnitPi> units_34(@Argument UnitInputProjection uinput) {
		QUnit q=QUnit.unit;
		Pageable pageable= PageRequest.of(0, 11);
		Predicate predicate=null;
		predicate = q.cancel.eq(uinput.getCancel())
				.and(q.indCod.startsWithIgnoreCase(uinput.getIndCod()));
		//Map<String, ? extends Expression<?>> mapFieldBind=null;
		//Map<String,Expression<?>>  mapFieldBind= createBindings(beanArgs.toArray(new Expression<?>[0]));
		QBeanMy<?> piExp=new QBeanMy<Unit>(Unit.class, q.id,q.indCod,q.cancel,q.area
				,q.company.id,q.company.name ,q.person.id,q.person.name);
		piExp.bindLeftJoin(q.company,q.person);
		//QBeanMy<?>  piExp=new QBeanMy<Unit>(Unit.class,q.id,q.indCod,q.cancel,q.company.id,q.company.name);
		//Slice<UnitPi> slice= unitRepository.readAllBy(pageable, UnitPi.class); //unitRepository.<UnitPi,Slice<UnitPi>>findBy();
		//List<String> properties= Lists.newArrayList("id","indCod","cancel","company.id","company.name");
		Slice<UnitPi> slice= units.findBy(piExp, predicate, (query)-> {
			FluentQuery.FetchableFluentQuery<UnitPi> queryUse = query.as(UnitPi.class);    //返回类型要求; <R> FetchableFluentQuery<R> as(Class<R> resultType); UnitPi.class
			//queryUse= queryUse.project("id","indCod","cancel"); //没影响到select！但是影响返回可用字段，所以关联字段1+N可能会被抑制掉。对父类findBy有好处，自定做findBy就没啥用了。
			return  queryUse.page(pageable);
		});
		//执行save()/delete()首先会从数据库查询最新数据的(所有实体字段)以及非懒加载实体,事务提交时会自动update set company_id=?,会包括所有实体字段的以及此次修改的关联实体。
		return slice;
	}

	//@SchemaMapping(typeName="User", field="id")
	//public String getId(User user) { return user.getId() + "扩展变dwe212";	}
}


//,company:{id:"04439cb4-9e13-43f3-ae58-0371d124a8dd",name:"福建省福州市",no:"694359076"}
@ProjectedPayload
interface UnitInputProjection {
	Boolean getCancel();
	String getIndCod();

}

